use core::future::ready;
use std::{future::Ready, pin::Pin, rc::Rc};

use actix_web::{
    Error, FromRequest, HttpMessage,
    dev::{Service, ServiceRequest, ServiceResponse, Transform},
};

use crate::core::{
    common::AppError,
    token_manage::{self, Claims},
};

pub struct AuthMiddleware;

impl<S, B> Transform<S, ServiceRequest> for AuthMiddleware
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S: 'static,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;

    type Error = Error;

    type Transform = AuthService<S>;

    type InitError = ();

    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(AuthService {
            service: Rc::new(service),
        }))
    }
}

pub struct AuthService<S: 'static> {
    service: Rc<S>,
}

impl<S, B> Service<ServiceRequest> for AuthService<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
{
    type Response = ServiceResponse<B>;

    type Error = Error;

    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;

    fn poll_ready(
        &self,
        ctx: &mut core::task::Context<'_>,
    ) -> std::task::Poll<Result<(), Self::Error>> {
        dbg!("auth service pool_ready!");
        self.service.poll_ready(ctx)
    }

    fn call(&self, req: ServiceRequest) -> Self::Future {
        let service = Rc::clone(&self.service);
        Box::pin(async move {
            let token_str = req
                .headers()
                .get("Authorization")
                .and_then(|h| h.to_str().ok())
                .and_then(|s| s.strip_prefix("Bearer ").map(|s| s.to_string()));

            dbg!(&token_str);

            let t = match token_str {
                None => {
                    let err = AppError::UnAuthorized("not found token from headers".into());
                    let err = actix_web::Error::from(err);
                    return Err(err);
                }
                Some(token) => token,
            };
            dbg!(&t);
            match token_manage::validate_jwt(t) {
                Ok(claims) => {
                    dbg!(&claims);
                    req.extensions_mut().insert(claims);
                }
                Err(err) => {
                    let err =
                        AppError::UnAuthorized(format!("validate token failed! {err}").into());
                    dbg!(&err);
                    let err = actix_web::Error::from(err);
                    return Err(err);
                }
            }

            service.call(req).await
        })
    }
}

impl FromRequest for Claims {
    type Error = actix_web::Error;

    type Future = Ready<Result<Self, Self::Error>>;

    fn from_request(
        req: &actix_web::HttpRequest,
        _payload: &mut actix_web::dev::Payload,
    ) -> Self::Future {
        let claims = req.extensions().get::<Claims>().cloned();
        match claims {
            Some(claims) => ready(Ok(claims)),
            None => ready(Err(actix_web::error::ErrorUnauthorized("not auhtorized!"))),
        }
    }
}
